#include"ImageProcess.h"
const string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string Base64Tochar::base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) //string转base64
{
	std::string ret;
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];
	while (in_len--) {
		char_array_3[i++] = *(bytes_to_encode++);
		if (i == 3) {
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;
			for (i = 0; (i <4); i++)
				ret += base64_chars[char_array_4[i]];
			i = 0;
		}
	}

	if (i)
	{
		for (j = i; j < 3; j++)
			char_array_3[j] = '\0';

		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
		char_array_4[3] = char_array_3[2] & 0x3f;
		for (j = 0; (j < i + 1); j++)
			ret += base64_chars[char_array_4[j]];
		while ((i++ < 3))
			ret += '=';

	}

	return ret;

}

string Base64Tochar::base64_decode(string const& encoded_string)  //base64转string
{
	int in_len = encoded_string.size();
	int i = 0;
	int j = 0;
	int in_ = 0;
	unsigned char char_array_4[4], char_array_3[3];
	std::string ret;
	while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
		char_array_4[i++] = encoded_string[in_]; in_++;
		if (i == 4) {
			for (i = 0; i <4; i++)
				char_array_4[i] = base64_chars.find(char_array_4[i]);
			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
			for (i = 0; (i < 3); i++)
				ret += char_array_3[i];
			i = 0;
		}
	}
	if (i) {
		for (j = i; j <4; j++)
			char_array_4[j] = 0;
		for (j = 0; j <4; j++)
			char_array_4[j] = base64_chars.find(char_array_4[j]);
		char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
		char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
		char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
		for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
	}

	return ret;

}

void CImageProcess::AdaptiveThreshold(unsigned char* input, unsigned char*& bin, int width, int height, int S, int T) //图像二值化函数
//函数说明:图像自适应二值化
//参数说明：input [in] 源图像数据
//          bin   [out] 目标图像数据
//          width [in] 图片的宽
//          height[in] 图片的高
{
	////int S = width >> 3;
	//int S = 10;

	//int T = 35;

	unsigned long* integralImage = NULL;
	int i, j;
	long sum = 0;
	int count = 0;
	int index;
	int x1, y1, x2, y2;
	int s2 = S / 2;

	bin = new unsigned char[width*height];
	// Create  the Integral Image
	integralImage = (unsigned long*)malloc(width*height*sizeof(unsigned long*));
	for (i = 0; i < width; i++)
	{
		//reset this column sum
		sum = 0;
		for (j = 0; j < height; j++)
		{
			index = j*width + i;
			sum += input[index];
			if (i == 0)
			{
				integralImage[index] = sum;
			}
			else
			{
				integralImage[index] = integralImage[index - 1] + sum;
			}
		} // for j
	} //for i

	// perform thresholdingg
	for (i = 0; i < width; i++)
	{
		for (j = 0; j < height; j++)
		{
			index = j*width + i;
			//set S x S region
			x1 = i - s2; x2 = i + s2;
			y1 = j - s2; y2 = j + s2;
			//check the border
			if (x1 < 0) x1 = 0;
			if (x2 >= width) x2 = width - 1;
			if (y1 < 0) y1 = 0;
			if (y2 >= height) y2 = height - 1;
			count = (x2 - x1)*(y2 - y1);
			// I(x,y)=s(x2,y2)-s(x1,y2)-s(x2,y1)+s(x1,x1)
			sum = integralImage[y2*width + x2] -
				integralImage[y1*width + x2] -
				integralImage[y2*width + x1] +
				integralImage[y1*width + x1];
			if ((long)(input[index] * count) < (long)(sum*(100 - T) / 100))
			{
				bin[index] = 0;
			}
			else
			{
				bin[index] = 255;
			}
		}// for j
	}// for i
	free(integralImage);
}
void CImageProcess::UnmaskSharp(Mat& srcImage, Mat& dstImage, Size& size, int k)
{

	Mat temp;
	if (srcImage.channels() == 3)
        cvtColor(srcImage, temp, cv::COLOR_RGB2GRAY);
	else
		srcImage.copyTo(temp);
	temp = 255 - temp;
	Mat blurImage;
	GaussianBlur(temp, blurImage, size, 3);
	Mat mask = temp - blurImage;
	dstImage = temp + k * mask;
	dstImage = 255 - dstImage;
}
void CImageProcess::background_remove(Mat& srcImage, Mat& dstImage, int size) //去除图像背景
//函数功能：去除图像背景
//参数说明：srcImage[in]:原始图片
//          dstImage[out]:输出图片
{
	Mat temp, blur_image;
	Mat temp_dstimage;
	Mat img32, pattern32;//32位浮点型Mat
	if (srcImage.channels() != 1)
        cvtColor(srcImage, srcImage, COLOR_RGB2GRAY);
	srcImage.copyTo(temp);

	blur(temp, blur_image, Size(temp.cols / size, temp.rows / size));
	//medianBlur(temp, blur_image)
	temp.convertTo(img32, CV_32F);
	blur_image.convertTo(pattern32, CV_32F);
	temp_dstimage = 1 - (img32 / pattern32);
	temp_dstimage = temp_dstimage * 255;
	temp_dstimage.convertTo(temp_dstimage, CV_8U);
	temp_dstimage = 255 - temp_dstimage;
	temp_dstimage.copyTo(dstImage);
}
Mat CImageProcess::getImageData(string imagedata) //更新图片数据
{
	Mat temp_image;
	if (imagedata == "")
		return temp_image;

	char* imgdata = NULL;
	Mat src_image_temp;
	imgdata = new char[imagedata.length() + 1];
	memcpy(imgdata, imagedata.c_str(), imagedata.length() + 1);
    src_image_temp = imdecode(Mat(1, imagedata.length()*sizeof(imagedata[0]), CV_8UC1, imgdata), IMREAD_COLOR); //IMREAD_GRAYSCALE
	//src_image_temp = imdecode()
	if (src_image_temp.channels() != 1)
        cvtColor(src_image_temp, temp_image, COLOR_RGB2GRAY);
	else
		src_image_temp.copyTo(temp_image);
	delete[]imgdata;

	return temp_image;
}
void CImageProcess::encode_image(Mat& srcImage,string& imagedata_base64)  //将Mat转化成jpg
{
	vector<uchar> buf;
	try{
		imencode(".jpg", srcImage, buf, vector<int>());
	}
	catch (...)
	{
		imagedata_base64 = "";
		
		return;
	}
	unsigned char* tempimagedata = NULL;
	tempimagedata = (unsigned char*)realloc(tempimagedata, buf.size());//分配内存，保存图像数据
	memcpy(tempimagedata, &buf[0], buf.size());
	if (imagedata_base64 != "")
		imagedata_base64 = "";
	imagedata_base64 = m_base642char.base64_encode(tempimagedata, buf.size());
	free(tempimagedata); //释放内存
	tempimagedata = NULL;
}
void CImageProcess::ComputeAngle(const Point& leftpeak, const Point& rightpeak,double& angle) //计算倾斜角度
//参数说明：
// leftpeak[in]:答题卡左上角顶点坐标
// rightpeak[in]:答题卡右上角顶点坐标
{
	angle = 0.0;
	angle = atan2(rightpeak.y - leftpeak.y, rightpeak.x - leftpeak.x);
	angle = angle * 180 / CV_PI;
	return;
}
void CImageProcess::deskew(Mat& srcImage,double angle) //图片旋转函数
// 参数说明
// srcImage[in/out]:in:原始图片;out:旋转后的图片
{
	Mat dstImage;
	if (angle == 0.0)
	{
		return;
	}

	cv::bitwise_not(srcImage, srcImage);

	Point center;  //旋转中心
	center.x = srcImage.cols / 2;
	center.y = srcImage.rows / 2;
	cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1);

	cv::Mat rotated;

	cv::warpAffine(srcImage, dstImage, rot_mat, srcImage.size(), cv::INTER_LINEAR); //287ms  INTER_NEAREST,INTER_CUBIC ,INTER_LINEAR

	cv::bitwise_not(dstImage, srcImage);
	return;
}
bool CImageProcess::image_process(string& src_image_path, string& result_image_path)
{
	Mat result_image;
	Mat srcImage = imread(src_image_path,0);  //字符串转Mat
	if (srcImage.channels() == 3)
        cvtColor(srcImage, result_image, COLOR_BGR2GRAY);
	else
		srcImage.copyTo(result_image);
	background_remove(result_image, result_image, 3); //去除图像背景
    Size size(3, 3);
    UnmaskSharp(result_image, result_image, size, 5);//图像锐化增强

    /*uchar *data = NULL;
	AdaptiveThreshold(result_image.data, data, result_image.cols, result_image.rows, result_image.cols / 8, 8);
	memcpy(result_image.data, data, sizeof(unsigned char)*result_image.cols*result_image.rows);
	if (data != NULL)
		delete data;
    RemoveSmallRegion(result_image, result_image, 5, 0, 0);*/
	imwrite(result_image_path, result_image);
	return true;
}
bool CImageProcess::image_process(string& src_image_data_base64, Point left_top_point, Point right_top_point, string result_image_data_base64)
//图像预处理接口
//参数说明
//src_image_data_base64[in]:原始图片数据，base64字符串
//left_top_point[in]:       图片左上角坐标点
//right_top_point[in]:      图片右上角坐标点
//result_image_data_base64  结果图片数据，base64字符串
{
	string imagedata;
	Mat result_image;
	
	double angle = 0.0;

	imagedata = m_base642char.base64_decode(src_image_data_base64);
	Mat srcImage = getImageData(imagedata);  //字符串转Mat
	if (srcImage.channels() == 3)
        cvtColor(srcImage, result_image, COLOR_BGR2GRAY);
	else
		srcImage.copyTo(result_image);
	ComputeAngle(left_top_point, right_top_point, angle);
	deskew(result_image, angle);
	background_remove(result_image, result_image, 3); //去除图像背景
    Size size(3, 3);
    UnmaskSharp(result_image, result_image, size, 5);//图像锐化增强

	uchar *data = NULL;
	AdaptiveThreshold(result_image.data, data, result_image.cols, result_image.rows, result_image.cols / 8, 8);
	memcpy(result_image.data, data, sizeof(unsigned char)*result_image.cols*result_image.rows);
	if (data != NULL)
		delete data;
	RemoveSmallRegion(result_image, result_image, 5, 0, 0);
	encode_image(result_image, result_image_data_base64);  //将Mat转化成jpg
	return true;
}
void CImageProcess::RemoveSmallRegion(Mat& Src, Mat& Dst, int AreaLimit, int CheckMode, int NeihborMode)
//函数功能：孔洞填充，小区域去除
//参数说明：Src[in]:原始图像
//          Dst[out]:输出图像
//          AreaLimit[in]:最小区域
//          CheckMode[in]: 0代表去除黑区域，1代表去除白区域;
//          NeihborMode[in]：0代表4邻域，1代表8邻域
{
	int RemoveCount = 0;       //记录除去的个数  
	Mat temp = Mat::zeros(Src.size(), CV_8UC1);
	//记录每个像素点检验状态的标签，0代表未检查，1代表正在检查,2代表检查不合格（需要反转颜色），3代表检查合格或不需检查    
	Mat Pointlabel = Mat::zeros(Src.size(), CV_8UC1);

	if (CheckMode == 1)
	{
		for (int i = 0; i < Src.rows; ++i)
		{
			uchar* iData = Src.ptr<uchar>(i);
			uchar* iLabel = Pointlabel.ptr<uchar>(i);
			for (int j = 0; j < Src.cols; ++j)
			{
				if (iData[j] < 10)
				{
					iLabel[j] = 3;
				}
			}
		}
	}
	else
	{
		for (int i = 0; i < Src.rows; ++i)
		{
			uchar* iData = Src.ptr<uchar>(i);
			uchar* iLabel = Pointlabel.ptr<uchar>(i);
			for (int j = 0; j < Src.cols; ++j)
			{
				if (iData[j] > 10)
				{
					iLabel[j] = 3;
				}
			}
		}
	}

	vector<Point2i> NeihborPos;  //记录邻域点位置    
	NeihborPos.push_back(Point2i(-1, 0));
	NeihborPos.push_back(Point2i(1, 0));
	NeihborPos.push_back(Point2i(0, -1));
	NeihborPos.push_back(Point2i(0, 1));
	if (NeihborMode == 1)
	{
		NeihborPos.push_back(Point2i(-1, -1));
		NeihborPos.push_back(Point2i(-1, 1));
		NeihborPos.push_back(Point2i(1, -1));
		NeihborPos.push_back(Point2i(1, 1));
	}
	int NeihborCount = 4 + 4 * NeihborMode;
	int CurrX = 0, CurrY = 0;
	//开始检测    
	for (int i = 0; i < Src.rows; ++i)
	{
		uchar* iLabel = Pointlabel.ptr<uchar>(i);
		for (int j = 0; j < Src.cols; ++j)
		{
			if (iLabel[j] == 0)
			{
				//********开始该点处的检查**********    
				vector<Point2i> GrowBuffer;                                      //堆栈，用于存储生长点    
				GrowBuffer.push_back(Point2i(j, i));
				Pointlabel.at<uchar>(i, j) = 1;
				int CheckResult = 0;                                               //用于判断结果（是否超出大小），0为未超出，1为超出    

				for (int z = 0; z<GrowBuffer.size(); z++)
				{

					for (int q = 0; q<NeihborCount; q++)                                      //检查四个邻域点    
					{
						CurrX = GrowBuffer.at(z).x + NeihborPos.at(q).x;
						CurrY = GrowBuffer.at(z).y + NeihborPos.at(q).y;
						if (CurrX >= 0 && CurrX<Src.cols&&CurrY >= 0 && CurrY<Src.rows)  //防止越界    
						{
							if (Pointlabel.at<uchar>(CurrY, CurrX) == 0)
							{
								GrowBuffer.push_back(Point2i(CurrX, CurrY));  //邻域点加入buffer    
								Pointlabel.at<uchar>(CurrY, CurrX) = 1;           //更新邻域点的检查标签，避免重复检查    
							}
						}
					}

				}
				if (GrowBuffer.size()>AreaLimit) CheckResult = 2;                 //判断结果（是否超出限定的大小），1为未超出，2为超出    
				else { CheckResult = 1;   RemoveCount++; }
				for (int z = 0; z<GrowBuffer.size(); z++)                         //更新Label记录    
				{
					CurrX = GrowBuffer.at(z).x;
					CurrY = GrowBuffer.at(z).y;
					Pointlabel.at<uchar>(CurrY, CurrX) += CheckResult;
				}
				//********结束该点处的检查**********    


			}
		}
	}

	CheckMode = 255 * (1 - CheckMode);
	//开始反转面积过小的区域    
	for (int i = 0; i < Src.rows; ++i)
	{
		uchar* iData = Src.ptr<uchar>(i);
		uchar* iDstData = temp.ptr<uchar>(i);
		uchar* iLabel = Pointlabel.ptr<uchar>(i);
		for (int j = 0; j < Src.cols; ++j)
		{
			if (iLabel[j] == 2)
			{
				iDstData[j] = CheckMode;
			}
			else if (iLabel[j] == 3)
			{
				iDstData[j] = iData[j];
			}
		}
	}
	temp.copyTo(Dst);
	temp.release();
}
